﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
using YunSpace.Core;

namespace YunSpace.ThirdService.WeChat
{
    /// <summary>
    /// 微信管理类
    /// </summary>
    public class WeiXinUtil
    {
        #region 1. 参数

        public static string APIKey
        {
            get
            {
                return Configs.GetValue("APIKey");
            }
        }



        public static string AppID
        {
            get
            {
                return Configs.GetValue("AppID");
            }
        }

        public static string MchID
        {
            get
            {
                return Configs.GetValue("MchID");
            }
        }

        public static string WebSiteUrl
        {
            get
            {
                return Configs.GetValue("WebSiteUrl");
            }
        }

        #endregion

        #region 2. 下单API

        /// <summary>
        /// Builds the request.
        /// </summary>
        /// <returns></returns>
        public static string BuildRequest(string amount, string detail)
        {
            var dicParam = CreateParam(amount, detail);

            var signString = CreateURLParamString(dicParam);
            var preString = signString + "&key=" + APIKey;
            var sign = Sign(preString, "utf-8").ToUpper();
            dicParam.Add("sign", sign);

            return BuildForm(dicParam);
        }

        /// <summary>
        /// Generates the out trade no.
        /// </summary>
        /// <returns></returns>
        private static string GenerateOutTradeNo()
        {
            var ran = new Random();
            return $"{MchID}{DateTime.Now:yyyyMMddHHmmss}{ran.Next(999)}";
        }

        /// <summary>
        /// Signs the specified prestr.
        /// </summary>
        /// <param name="prestr">The prestr.</param>
        /// <param name="_input_charset">The input charset.</param>
        /// <returns></returns>
        private static string Sign(string prestr, string _input_charset)
        {
            var sb = new StringBuilder(32);
            MD5 md5 = new MD5CryptoServiceProvider();
            var t = md5.ComputeHash(Encoding.GetEncoding(_input_charset).GetBytes(prestr));
            foreach (var t1 in t)
            {
                sb.Append(t1.ToString("x").PadLeft(2, '0'));
            }

            return sb.ToString();
        }

        /// <summary>
        /// Creates the parameter.
        /// </summary>
        /// <returns></returns>
        private static SortedDictionary<string, string> CreateParam(string amount, string detail)
        {
            //const string amount = "1";
            //const string detail = "xxxx";
            double dubamount;
            double.TryParse(amount, out dubamount);
            var notify_url = WebSiteUrl; //支付完成后的回调处理页面
            
            var dic = new SortedDictionary<string, string>
            {
                {"appid", AppID},//账号ID
                {"mch_id", MchID},//商户号
                {"nonce_str", Guid.NewGuid().ToString().Replace("-", "")},//随机字符串
                {"body", detail}, //商品描述
                {"out_trade_no", GenerateOutTradeNo()},//商户订单号
                {"total_fee", (dubamount * 100).ToString(CultureInfo.InvariantCulture)},//总金额
                {"spbill_create_ip", Net.Ip },//终端IP
                {"notify_url", notify_url},//通知地址
                {"trade_type", "APP"}//交易类型 JSAPI、NATIVE、APP
            };

            return dic;
        }

        /// <summary>
        /// Creates the URL parameter string.
        /// </summary>
        /// <param name="dicArray">The dic array.</param>
        /// <returns></returns>
        private static string CreateURLParamString(SortedDictionary<string, string> dicArray)
        {
            var prestr = new StringBuilder();
            foreach (var temp in dicArray.OrderBy(o => o.Key))
            {
                prestr.Append(temp.Key + "=" + temp.Value + "&");
            }

            var nLen = prestr.Length;
            prestr.Remove(nLen - 1, 1);
            return prestr.ToString();
        }

        /// <summary>
        /// Builds the form.
        /// </summary>
        /// <param name="dicParam">The dic parameter.</param>
        /// <returns></returns>
        private static string BuildForm(SortedDictionary<string, string> dicParam)
        {
            var sbXML = new StringBuilder();
            sbXML.Append("<xml>");
            foreach (var temp in dicParam)
            {
                sbXML.Append("<" + temp.Key + ">" + temp.Value + "</" + temp.Key + ">");
            }

            sbXML.Append("</xml>");
            return sbXML.ToString();
        }

        /// <summary>
        /// Froms the XML.
        /// </summary>
        /// <param name="xml">The XML.</param>
        /// <returns></returns>
        /// <exception cref="Exception">将空的xml串转换为WxPayData不合法!</exception>
        public static SortedDictionary<string, string> FromXml(string xml)
        {
            var sortDic = new SortedDictionary<string, string>();
            if (string.IsNullOrEmpty(xml))
            {
                throw new Exception("将空的xml串转换为WxPayData不合法!");
            }

            var xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xml);
            var xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
            var nodes = xmlNode.ChildNodes;
            foreach (XmlNode xn in nodes)
            {
                var xe = (XmlElement)xn;

                if (!sortDic.ContainsKey(xe.Name))
                    sortDic.Add(xe.Name, xe.InnerText);
            }
            return sortDic;
        }

        /// <summary>
        /// Posts the specified URL.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <param name="content">The content.</param>
        /// <param name="contentType">Type of the content.</param>
        /// <returns></returns>
        /// <exception cref="Exception">POST请求错误" + e</exception>
        public static string Post(string url, string content, string contentType = "application/x-www-form-urlencoded")
        {
            string result;
            try
            {
                using (var client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Accept.Clear();
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(contentType));
                    var stringContent = new StringContent(content, Encoding.UTF8);
                    var response = client.PostAsync(url, stringContent).Result;
                    result = response.Content.ReadAsStringAsync().Result;
                }
            }
            catch (Exception e)
            {
                throw new Exception("POST请求错误" + e);
            }
            return result;
        }

        #endregion

        #region 3. 生成预付订单，获取prepay_id。

        /// <summary>
        /// Gets the value from dic.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dic">The dic.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public static T GetValueFromDic<T>(IDictionary<string, string> dic, string key)
        {
            string val;
            dic.TryGetValue(key, out val);

            var returnVal = default(T);
            if (val != null)
                returnVal = (T)Convert.ChangeType(val, typeof(T));

            return returnVal;
        }

        /// <summary>
        /// Builds the application pay.
        /// </summary>
        /// <param name="prepayid">The prepayid.</param>
        /// <returns></returns>
        public static string BuildAppPay(string prepayid)
        {
            var dicParam = CreateWapAndAppPayParam(prepayid);
            var signString = CreateURLParamString(dicParam);
            var preString = signString + "&key=" + APIKey;

            var sign = Sign(preString, "utf-8").ToUpper();
            dicParam.Add("sign", sign);

            return (new
            {
                appid = dicParam["appid"],
                partnerid = dicParam["partnerid"],
                prepayid = dicParam["prepayid"],
                package = dicParam["package"],
                noncestr = dicParam["noncestr"],
                timestamp = dicParam["timestamp"],
                sign = dicParam["sign"]
            }).ToJson();
        }

        /// <summary>
        /// Creates the wap and application pay parameter.
        /// </summary>
        /// <param name="prepayId">The prepay identifier.</param>
        /// <returns></returns>
        private static SortedDictionary<string, string> CreateWapAndAppPayParam(string prepayId)
        {
            var dic = new SortedDictionary<string, string>
        {
            {"appid", AppID},//公众账号ID
            {"partnerid", MchID},//商户号
            {"prepayid", prepayId},//预支付交易会话ID
            {"package", "Sign=WXPay"},//扩展字段
            {"noncestr", Guid.NewGuid().ToString().Replace("-", "")},//随机字符串
            {
                "timestamp",
                (Convert.ToInt32((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds)).ToString()
            }//时间戳
        };

            return dic;
        }

        /// <summary>
        /// Validatons the query result.
        /// </summary>
        /// <param name="dic">The dic.</param>
        /// <returns></returns>
        public static bool ValidatonQueryResult(SortedDictionary<string, string> dic)
        {
            var result = false;

            if (dic.ContainsKey("return_code") && dic.ContainsKey("return_code"))
            {
                if (dic["return_code"] == "SUCCESS" && dic["result_code"] == "SUCCESS")
                    result = true;
            }

            if (result) return true;

            var sb = new StringBuilder();
            foreach (var item in dic.Keys)
            {
                sb.Append(item + ":" + dic[item] + "|");
            }

            return false;
        }

        #endregion

        #region 4. 调用获取支付信息，给到APP发起支付操作。

        //        var requestXml = WeiXinUtil.BuildRequest();
        //        var resultXml = WeiXinUtil.Post("https://api.mch.weixin.qq.com/pay/unifiedorder", requestXml);

        //        var dic = WeiXinUtil.FromXml(resultXml);

        //        string returnCode;
        //        dic.TryGetValue("return_code", out returnCode);

        //        if (returnCode == "SUCCESS")
        //        {
        //            var prepay_id = WeiXinUtil.GetValueFromDic<string>(dic, "prepay_id");
        //            if (!string.IsNullOrEmpty(prepay_id))
        //            {
        //                var payInfo = JsonConvert.DeserializeObject<WeiXinUtil.WxPayModel>(WeiXinUtil.BuildAppPay(prepay_id));

        //        json.Add(new JProperty("appid", payInfo.appid));
        //                json.Add(new JProperty("partnerid", payInfo.partnerid));
        //                json.Add(new JProperty("prepayid", payInfo.prepayid));
        //                json.Add(new JProperty("package", payInfo.package));
        //                json.Add(new JProperty("noncestr", payInfo.noncestr));
        //                json.Add(new JProperty("timestamp", payInfo.timestamp));
        //                json.Add(new JProperty("sign", payInfo.sign));
        //                json.Add(new JProperty("code", 0));
        //                json.Add(new JProperty("msg", "成功"));
        //                return this.Jsonp(json.ToString());
        //    }
        //            else
        //            {
        //                json.Add(new JProperty("code", 40028));
        //                json.Add(new JProperty("msg", "支付错误:" + WeiXinUtil.GetValueFromDic<string>(dic, "err_code_des")));
        //                return this.Jsonp(json.ToString());
        //    }
        //}
        //        else
        //        {
        //            return this.Jsonp(ApiException.OrderFailed());
        //}

        #endregion

        #region 5. APP支付完成，获得回调信息

        //        var context = this.HttpContext;
        //        var request = context.Request;
        //        var verifyResult = false;
        //        var requestXml = WeiXinUtil.GetRequestXmlData(request);
        //        var dic = WeiXinUtil.FromXml(requestXml);
        //        var returnCode = WeiXinUtil.GetValueFromDic<string>(dic, "return_code");

        //        if (!string.IsNullOrEmpty(returnCode) && returnCode == "SUCCESS")//通讯成功
        //        {
        //            var result = WeiXinUtil.WePayNotifyValidation(dic);
        //            if (result)
        //            {
        //                var transactionid = WeiXinUtil.GetValueFromDic<string>(dic, "transaction_id");

        //                if (!string.IsNullOrEmpty(transactionid))
        //                {
        //                    var queryXml = WeiXinUtil.BuildQueryRequest(transactionid, dic);
        //        var queryResult = WeiXinUtil.Post("https://api.mch.weixin.qq.com/pay/orderquery", queryXml);
        //        var queryReturnDic = WeiXinUtil.FromXml(queryResult);

        //                    if (WeiXinUtil.ValidatonQueryResult(queryReturnDic))//查询成功
        //                    {
        //                        verifyResult = true;
        //                        var status = WeiXinUtil.GetValueFromDic<string>(dic, "result_code");

        //                        if (!string.IsNullOrEmpty(status) && status == "SUCCESS")
        //                        {
        //                            var order = new Order()
        //                            {
        //                                OrderNumber = WeiXinUtil.GetValueFromDic<string>(dic, "out_trade_no"),
        //                                TransactionId = transactionid,
        //                                ProductPrice = WeiXinUtil.GetValueFromDic<decimal>(dic, "total_fee") / 100,
        //                                TradeType = WeiXinUtil.GetValueFromDic<string>(dic, "trade_type"),
        //                                BankType = WeiXinUtil.GetValueFromDic<string>(dic, "bank_type"),
        //                                PayDate = DateTime.Parse(WeiXinUtil.GetValueFromDic<string>(dic, "time_end")),
        //                                StatusId = 1,
        //                                IsPresent = false,
        //                                AddDate = DateTime.Now,
        //                                IsDelete = false
        //                            };

        //        CURD.Add(order, ConfigHelper.WriteDB);

        //                            WeiXinUtil.BuildReturnXml("OK", "成功");
        //                        }
        //}
        //                    else
        //                        WeiXinUtil.BuildReturnXml("FAIL", "订单查询失败");
        //                }
        //                else
        //                    WeiXinUtil.BuildReturnXml("FAIL", "支付结果中微信订单号不存在");
        //            }
        //            else
        //                WeiXinUtil.BuildReturnXml("FAIL", "签名失败");
        //        }
        //        else
        //        {
        //            string returnmsg;
        //dic.TryGetValue("return_msg", out returnmsg);
        //            throw new Exception("异步通知错误：" + returnmsg);
        //        }

        //        return verifyResult;

        /// <summary>
        /// Gets the request XML data.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns></returns>
        public static string GetRequestXmlData(HttpRequest request)
        {
            var stream = request.InputStream;
            int count;
            var buffer = new byte[1024];
            var builder = new StringBuilder();
            while ((count = stream.Read(buffer, 0, 1024)) > 0)
            {
                builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
            }
            stream.Flush();
            stream.Close();

            return builder.ToString();
        }

        /// <summary>
        /// Wes the pay notify validation.
        /// </summary>
        /// <param name="dic">The dic.</param>
        /// <returns></returns>
        public static bool WePayNotifyValidation(SortedDictionary<string, string> dic)
        {
            var sign = GetValueFromDic<string>(dic, "sign");
            if (dic.ContainsKey("sign"))
            {
                dic.Remove("sign");
            }

            var tradeType = GetValueFromDic<string>(dic, "trade_type");
            var preString = CreateURLParamString(dic);

            if (string.IsNullOrEmpty(tradeType))
            {
                var preSignString = preString + "&key=" + APIKey;
                var signString = Sign(preSignString, "utf-8").ToUpper();
                return signString == sign;
            }
            else
                return false;
        }

        /// <summary>
        /// Builds the query request.
        /// </summary>
        /// <param name="transactionId">The transaction identifier.</param>
        /// <param name="dic">The dic.</param>
        /// <returns></returns>
        public static string BuildQueryRequest(string transactionId, SortedDictionary<string, string> dic)
        {
            var dicParam = CreateQueryParam(transactionId);
            var signString = CreateURLParamString(dicParam);
            var key = APIKey;
            var preString = signString + "&key=" + key;
            var sign = Sign(preString, "utf-8").ToUpper();
            dicParam.Add("sign", sign);

            return BuildForm(dicParam);
        }

        /// <summary>
        /// Builds the return XML.
        /// </summary>
        /// <param name="code">The code.</param>
        /// <param name="returnMsg">The return MSG.</param>
        /// <returns></returns>
        public static string BuildReturnXml(string code, string returnMsg)
        {
            return
                $"<xml><return_code><![CDATA[{code}]]></return_code><return_msg><![CDATA[{returnMsg}]]></return_msg></xml>";
        }

        public static SortedDictionary<string, string> CreateQueryParam(string transactionId)
        {
            var dicParam = new SortedDictionary<string, string> {
                { "appid",AppID},//账号ID
                { "mch_id", MchID},//商户号
                {"transaction_id",transactionId},//微信订单号
                {"nonce_str", Guid.NewGuid().ToString().Replace("-", "")},//随机字符串
            };
            return dicParam;
        }
        #endregion

    }
}
